////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "WNMemory/inc/WNMemory.h"
#include "WNCore/inc/WNAssert.h"
#include "WNMath/inc/WNPoint2.h"
#include "WNMath/inc/WNBasic.h"
#include "WNMath/inc/WNPoint3.h"
#include "WNMath/inc/WNVector2.h"
#include "WNMath/inc/WNVector3.h"
#include "WNMath/inc/WNVector4.h"

using namespace WNMath;
using namespace WNMemory;

// Constructors ////////////////////////////////////////////////////////////////////////////////////

WNPoint2::WNPoint2() :
    x(0.0f),
    y(0.0f) {
}

WNPoint2::WNPoint2(const WN_FLOAT32* _f) :
    x(_f[0]),
    y(_f[1]) {
}

WNPoint2::WNPoint2(WN_FLOAT32 _x, WN_FLOAT32 _y) :
    x(_x),
    y(_y) {
}

// Operators ///////////////////////////////////////////////////////////////////////////////////////

WNPoint2::operator WN_FLOAT32* () {
    return(&x);
}

WNPoint2::operator const WN_FLOAT32* () const {
    return(&x);
}

WN_FLOAT32& WNPoint2::operator () (WN_INT32 _index) {
    WN_DEBUG_ASSERT(_index < 2);

    return(*(&x + _index));
}

WN_FLOAT32 WNPoint2::operator () (WN_INT32 _index) const {
    WN_DEBUG_ASSERT(_index < 2);

    return *(&x + _index);
}

WNPoint2 WNPoint2::operator + () const {
    return(*this);
}

WNPoint2 WNPoint2::operator - () const {
    return(WNPoint2(-x, -y));
}

WNPoint2 WNPoint2::operator += (const WNVector2& _vector) {
    x += _vector.x;
    y += _vector.y;

    return(*this);
}

WNPoint2 WNPoint2::operator -= (const WNVector2& _vector) {
    x -= _vector.x;
    y -= _vector.y;

    return(*this);
}

WNPoint2 WNPoint2::operator + (const WNVector2& _vector) const {
    return(WNPoint2(x + _vector.x, y + _vector.y));
}

WNPoint2 WNPoint2::operator - (const WNVector2& _vector) const {
    return(WNPoint2(x - _vector.x, y - _vector.y));
}

WNVector2 WNPoint2::operator - (const WNPoint2& _point) const {
    return WNVector2(x - _point.x, y - _point.y);
}

WNVector2 WNPoint2::operator * (const WNPoint2& _point) const {
    return(WNVector2(x * _point.x, y * _point.y));
}

WN_BOOL WNPoint2::operator == (const WNPoint2& _point) const {
    return(0 == WNMemCmp(this, &_point, sizeof(WNPoint2)));
}

WN_BOOL WNPoint2::operator != (const WNPoint2& _point) const {
    return(0 != WNMemCmp(this, &_point, sizeof(WNPoint2)));
}

// Members /////////////////////////////////////////////////////////////////////////////////////////

WN_VOID WNPoint2::Zero() {
    WNMemSet(&x, 0, sizeof(WNPoint2));
}

WN_VOID WNPoint2::Snap(WNSnapDirection _direction) {
    const WNPoint2 point = *this;

    switch(_direction) {
    case WN_SNAP_NEAREST:
        x = WNRound(point.x);
        y = WNRound(point.y);

        break;
    case WN_SNAP_DOWN:
        x = WNFloor(point.x);
        y = WNFloor(point.y);

        break;
    case WN_SNAP_UP:
        x = WNCeil(point.x);
        y = WNCeil(point.y);

        break;
    case WN_SNAP_TRUNCATE:
        x = WNTrunc(point.x);
        y = WNTrunc(point.y);
    }
}

WN_VOID WNPoint2::Translate(WN_FLOAT32 _translation) {
    x += _translation;
    y += _translation;
}

WN_VOID WNPoint2::Translate(WN_FLOAT32 _x, WN_FLOAT32 _y) {
    x += _x;
    y += _y;
}

WN_VOID WNPoint2::Translate(const WNVector2& _translation) {
    *this += _translation;
}

WN_BOOL WNPoint2::IsZero() const {
    return(x == 0.0f && y == 0.0f);
}

WN_VOID WNPoint2::Set(const WN_FLOAT32* _f) {
    WNMemCpy(&x, &_f[0], sizeof(WNPoint2));
}

WN_VOID WNPoint2::Set(WN_FLOAT32 _x, WN_FLOAT32 _y) {
    x = _x;
    y = _y;
}

WNPoint2 WNPoint2::GetSnaped(WNSnapDirection _direction) const {
    WNPoint2 point = *this;

    return(point.Snap(_direction), point);
}

WNPoint2 WNPoint2::GetTranslated(WN_FLOAT32 _translation) const {
    WNPoint2 point = *this;

    return(point.Translate(_translation), point);
}

WNPoint2 WNPoint2::GetTranslated(WN_FLOAT32 _x, WN_FLOAT32 _y) const {
    WNPoint2 point = *this;

    return(point.Translate(_x, _y), point);
}

WNPoint2 WNPoint2::GetTranslated(const WNVector2& _translation) const {
    WNPoint2 point = *this;

    return(point.Translate(_translation), point);
}

WNPoint3 WNPoint2::ToPoint3() const {
    return(WNPoint3(x, y, 0.0f));
}

WNVector2 WNPoint2::ToVector2() const {
    return(WNVector2(x, y));
}

WNVector3 WNPoint2::ToVector3() const {
    return(WNVector3(x, y, 0.0f));
}

WNVector4 WNPoint2::ToVector4() const {
    return(WNVector4(x, y, 0.0f, 1.0f));
}